VS Code extension: Auto-restore on workspace open and config change#15546
VS Code extension: Auto-restore on workspace open and config change#15546adamint wants to merge 7 commits intomicrosoft:mainfrom
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15546Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15546" |
There was a problem hiding this comment.
Pull request overview
Adds an auto-restore mechanism to the VS Code extension so aspire restore is run automatically to keep integration packages in sync with aspire.config.json, reducing editor squiggles after branch switches/config edits.
Changes:
- Introduces
AspirePackageRestoreProviderto runaspire restoreon workspace open and onaspire.config.jsonchanges (with a concurrency cap and status bar progress). - Adds the
aspire.enableAutoRestoresetting (defaulttrue) plus localized strings for restore progress and results. - Updates TypeScript playground
aspire.config.jsonfiles and refreshes generated AppHost.modulesoutputs.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| playground/TypeScriptApps/RpsArena/aspire.config.json | Moves package versions to 13.2.0 and removes daily SDK/channel fields. |
| playground/TypeScriptApps/AzureFunctionsSample/aspire.config.json | Adds appHost metadata and moves package versions to 13.2.0. |
| playground/TypeScriptApps/AzureFunctionsSample/AppHost/.modules/transport.ts | Updates generated transport layer (cancellation, marshalling, connection/auth flow). |
| playground/TypeScriptApps/AzureFunctionsSample/AppHost/.modules/base.ts | Updates generated base SDK types (ReferenceExpression enhancements, transport value serialization). |
| playground/TypeScriptApps/AzureFunctionsSample/AppHost/.modules/.codegen-hash | Updates codegen hash to reflect regenerated modules. |
| extension/src/utils/settings.ts | Adds accessor for enableAutoRestore. |
| extension/src/utils/AspirePackageRestoreProvider.ts | New provider that runs/monitors aspire restore and shows status bar progress. |
| extension/src/loc/strings.ts | Adds localized runtime strings for restore progress/results. |
| extension/src/extension.ts | Wires up the new auto-restore provider during activation. |
| extension/package.nls.json | Adds localized description for the new setting and restore messages. |
| extension/package.json | Defines the new aspire.enableAutoRestore configuration setting. |
| extension/loc/xlf/aspire-vscode.xlf | Adds localization units for new strings/setting description. |
playground/TypeScriptApps/AzureFunctionsSample/AppHost/.modules/transport.ts
Outdated
Show resolved
Hide resolved
JamesNK
left a comment
There was a problem hiding this comment.
Code review — focused on problems only. 5 inline comments on the new AspirePackageRestoreProvider.
…g restores, separate errors, fix timeout leak, handle unhandled promise
Co-authored-by: Ankit Jain <radical@gmail.com>
… status bar - Fix floating promises in watcher handlers (void + catch) - Fix timing bug: .finally() runs before _hadFailure is updated - Extract _scheduleHide() for status bar auto-hide logic - Add explicit _showProgress() call on success path - Only update _lastContent after successful restore - Add settled flag to prevent double resolve/reject - Add try/catch around proc.kill() - Replace magic numbers with named constants - Fix space before ellipsis in localized strings - Wire up AspirePackageRestoreProvider in extension.ts - Add aspire-vscode.restore command - Show failure state in status bar with error background - Make status bar item clickable (runs restore command)
- Early returns (file-read failure, unchanged content) now increment _completed so the progress bar doesn't get stuck - Skip _total increment when a re-restore is already queued for the same directory, preventing counter inflation from duplicate events
1c0e42c to
8a23f34
Compare
- Guard counter mutations during batch with _batchRunning flag - Replace single _hadFailure boolean with per-directory _failedDirs set - Use single _hideTimeout reference instead of accumulating timeouts - Change recursive pending-restore tail call to iterative while loop Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| } | ||
| } | ||
|
|
||
| private async _runRestore(configDir: string, relativePath: string): Promise<void> { |
There was a problem hiding this comment.
Bug: _active leaks if getAspireCliExecutablePath() throws.
_active.set(configDir, ...) happens before the await that can throw, but the cleanup in .finally() is on the inner Promise that is never constructed when the CLI path resolution fails. This leaves configDir permanently stuck in _active, so:
- Every future watcher event for that directory takes the
_active.has(configDir)branch, adds to_pendingRestore, and returns immediately. - The
while (this._pendingRestore.delete(configDir))loop in the caller can spin infinitely — the pending restore re-enters_restoreIfChanged, sees the stale_activeentry, re-adds to_pendingRestore, returns, and thewhileloop keeps going.
Suggest wrapping the entire body of _runRestore in try/finally:
private async _runRestore(configDir: string, relativePath: string): Promise<void> {
this._active.set(configDir, relativePath);
this._showProgress();
try {
const cliPath = await this._terminalProvider.getAspireCliExecutablePath();
await new Promise<void>((resolve, reject) => {
// ... existing spawning logic ...
});
} finally {
this._active.delete(configDir);
this._completed++;
this._showProgress();
}
}| if (!this._pendingRestore.has(configDir) && !this._batchRunning) { | ||
| if (this._active.size === 0 && this._completed >= this._total) { | ||
| this._total = 1; | ||
| this._completed = 0; | ||
| this._failedDirs.clear(); | ||
| } else { | ||
| this._total++; |
There was a problem hiding this comment.
Bug: progress counters desync when a watcher fires during _restoreAll().
When _batchRunning is true, _total is not incremented here. But if the watcher's restore actually runs (e.g. different directory with no active restore), _completed still gets incremented in _runRestore's .finally(). This causes _completed > _total, so the status bar can show progress like "5/4 projects".
One fix: always increment _total when the restore will actually proceed, regardless of _batchRunning. Or track watcher-triggered restores with a separate counter from the batch.
| @@ -69,6 +70,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
| const settingsCommandRegistration = vscode.commands.registerCommand('aspire-vscode.settings', () => tryExecuteCommand('aspire-vscode.settings', terminalProvider, settingsCommand)); | |||
| const openLocalSettingsCommandRegistration = vscode.commands.registerCommand('aspire-vscode.openLocalSettings', () => tryExecuteCommand('aspire-vscode.openLocalSettings', terminalProvider, openLocalSettingsCommand)); | |||
| const openGlobalSettingsCommandRegistration = vscode.commands.registerCommand('aspire-vscode.openGlobalSettings', () => tryExecuteCommand('aspire-vscode.openGlobalSettings', terminalProvider, openGlobalSettingsCommand)); | |||
There was a problem hiding this comment.
Clicking the error status bar doesn't clear the error state.
The command runs sendAspireCommandToAspireTerminal('restore'), which executes in the integrated terminal — completely separate from AspirePackageRestoreProvider's internal state. Even if the manual terminal restore succeeds, _failedDirs is never cleared, so the error icon persists indefinitely. Only a file content change that triggers a successful automatic restore clears it.
Consider either:
- Having the restore command also clear
_failedDirsand re-trigger_restoreAll(), or - Wiring the status bar command to a method on
AspirePackageRestoreProviderinstead of the terminal command.
Description
Adds automatic
aspire restoreexecution in the VS Code extension to keep integration packages in sync withaspire.config.json.Problem: When switching git branches that have different Aspire integrations configured, the editor shows squiggly errors until you manually run
aspire restore. This is a common friction point during development.Solution: The new
AspirePackageRestoreProviderautomatically runsaspire restore:aspire.config.jsonfiles found in the workspaceFileSystemWatchermonitorsaspire.config.jsonfiles; when content actually changes (e.g., branch switch, manual edit), it runs restoreFeatures
Running aspire restore (2/5 projects) ...)aspire restorecommand in the Aspire terminalAspire: Restorecommand — new command palette entry (aspire-vscode.restore) to manually trigger restoreaspire.config.jsoncontent actually changes, not on every file-save event_lastContentis only updated after a successful restore, so failures don't prevent retries on the next changeaspire.enableAutoRestoresetting (default:true); toggling the setting on immediately starts watching and restoringTesting
I tested this using:
Checklist